//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft shared
// source or premium shared source license agreement under which you licensed
// this source code. If you did not accept the terms of the license agreement,
// you are not authorized to use this source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the SOURCE.RTF on your install media or the root of your tools installation.
// THE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES.
//
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
PARTICULAR PURPOSE.

Module Name:    COMPACTOR.C

Abstract:       Compactor module

Notes:          The following module is responsible for compacting the FLASH media blocks.
                A compaction operation works as follows (using psuedo-code):
                 
                   foreach(physicalSector in Block b)
                   {
                        if(IsSectorMapped(physicalSector))
                        {
                            ReadSectorData(physicalSector, buffer);
                            newPhysicalSectorAddr = SM_GetNextFreeSector();
                            WriteSectorData(newPhysicalSector, buffer);
                            UpdateLogicalToPhysicalMapping(logicalSectorAddr, newPhysicalSectorAddr);   
                            MarkSectorDirty(physicalSector);
                        }

                        if(IsSectorDirty(physicalSector)
                        {
                            dwNumDirtySectorsFound++;
                        }
                    }
                    EraseBlock(Block b);
                    AddFreeSectorsToSectorManager(Block b);

                That is, to compact a block we just need to move any good data to another physical location 
                on the media and simply ignore any DIRTY physical sectors.  Once the entire block has been 
                processed, it can then safely be erased.  Notice also that all of the good data is still 
                retained after the ERASE operation and that data CANNOT be lost during a power-failure situation 
                because the old data is marked DIRTY after the data has been safely moved to another physical 
                sector address.   

                There are several important points to consider in this algorithm:

                    1)  The compactor uses the *exact* same code to move good data to another portion of the 
                        FLASH media as the FAL uses to write new data from the file system.  Practically, this 
                        means that the compactor queries the Sector Manager for the next FREE physical sector 
                        when it needs to perform a WRITE operation.

                    2)  The algorithm takes the necessary precautions to avoid *any* data loss during a 
                        power-failure condition.  In the worst case scenario, the FLASH media will end up with 
                        two separate physical copies of the SAME data, which will be cleaned up by the FAL during 
                        the next boot sequence.

                    3)  The algorithm is simple.  Notice that there are NO special situations or end cases to consider.  
                        The Compactor simply moves good data to another potion of the disk (a.k.a. compacting the data) 
                        and then erases the block.
                        
                The Compactor *always* processes FLASH blocks consecutively.  Once the Compactor reaches the last 
                FLASH block on the media, it simply moves back to the first FLASH block and continues as necessary.

-----------------------------------------------------------------------------*/

#include "compactor.h"

extern DWORD g_dwCompactionPrio256;

/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Function:       Compactor::InitCompactor()

Description:    Initializes the compactor. 

Notes:          The compactor is not started by default.  The caller must invoke
                Compactor::StartCompactor() to start the compactor thread.

Returns:        Boolean indicating success.
------------------------------------------------------------------------------*/
BOOL Compactor::Init(PFlashRegion pRegion, FileSysFal* pFal)
{
    m_pRegion = pRegion;
    m_pFal = pFal;
    m_pSectorMgr = pFal->m_pSectorMgr;
    m_pMap = pFal->m_pMap;
    m_compactingBlockID = pRegion->dwStartPhysBlock;
    m_bEndCompactionThread = FALSE;

    if (pRegion->dwCompactBlocks < MINIMUM_FLASH_BLOCKS_TO_RESERVE)
    {
        ReportError((TEXT("FLASHDRV.DLL:Compactor::InitCompactor() - Invalid number of compaction blocks in region.\r\n")));
        goto INIT_ERROR;
    }
    
    //----- 1. Create the event that controls the compactor -----
    if((m_hStartCompacting = CreateEvent(NULL, FALSE, FALSE, NULL)) == NULL)
    {
        ReportError((TEXT("FLASHDRV.DLL:Compactor::InitCompactor() - Unable to create compactor control event object!!!\r\n")));
        goto INIT_ERROR;
    }

    //----- 2. Create the event that signals when compacting is complete -----
    if((m_hCompactionComplete = CreateEvent(NULL, FALSE, FALSE, NULL)) == NULL)
    {
        ReportError((TEXT("FLASHDRV.DLL:Compactor::InitCompactor() - Unable to create compactor completion event object!!!\r\n")));
        goto INIT_ERROR;
    }


    //----- 4. Allocate a buffer to use for moving MAPPED data during a compaction -----
    if((m_pSectorData = (PBYTE)LocalAlloc(LMEM_FIXED, g_pFlashMediaInfo->dwDataBytesPerSector)) == NULL)
    {
        ReportError((TEXT("FLASHDRV.DLL:Compactor::InitCompactor() - Unable to allocate memory for sector data.\r\n")));
        goto INIT_ERROR;
    }

    //----- 5. Initialize the "nested compactions" counter.  This counter is used to prevent an infinite -----
    //         recursion situation that can occur if WRITE operations keep failing within Compactor::CompactBlock()
    m_dwNestedCompactions = 0;

    //----- 6. Retrieve the ID for the block that we need to process on the next compaction -----
    //         NOTE: Currently, the FAL computes the block ID during system initialization and
    //               calls Compactor::SetCompactingBlock() appropriately.  Hence, we don't need to explicitly
    //               store/retrieve this state information on the FLASH media.


    //----- 7. Create the compactor thread -----
    if((m_hCompactorThread = CreateThread(NULL, 0, ::CompactorThread, (LPVOID)this, 0, &m_dwThreadID)) == NULL)
    {
        ReportError((TEXT("FLASHDRV.DLL:Compactor::InitCompactor() - Unable to create compactor thread!!!\r\n")));
        goto INIT_ERROR;
    }

	m_piWtCntAddS_A=(int*)LocalAlloc(LMEM_FIXED, pRegion->dwSectorsPerBlock*sizeof(int));
	m_piWtCntAddS_B=(int*)LocalAlloc(LMEM_FIXED, pRegion->dwSectorsPerBlock*sizeof(int));;
	if((!m_piWtCntAddS_A) || (!m_piWtCntAddS_B))
	{
        goto INIT_ERROR;
	}

    return TRUE;

INIT_ERROR:
    CloseHandle(m_hStartCompacting);
    CloseHandle(m_hCompactionComplete);
    CloseHandle(m_hCompactorThread);
    return FALSE;
}


/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Function:       Compactor::DeinitCompactor()

Description:    Deinitializes the compactor. 

Notes:          The compactor is first stopped.

Returns:        Boolean indicating success.
------------------------------------------------------------------------------*/
BOOL Compactor::Deinit()
{
    // By entering the device critical section, we are ensuring the current compaction 
    // operation is complete
    EnterCriticalSection(&g_csFlashDevice);
    
    m_dwFreeSectorsNeeded = 0;
    m_bEndCompactionThread = TRUE;
    SetEvent (m_hStartCompacting);
    
    LeaveCriticalSection(&g_csFlashDevice);

    WaitForSingleObject(m_hCompactorThread, 10000);
    
    //----- 2. Free all the compactor thread's system resources -----
    if(LocalFree(m_pSectorData) != NULL)
    {
        ReportError((TEXT("FLASHDRV.DLL:Compactor::Deinit() - Unable to free buffer used for moving MAPPED data\r\n")));
    }
    
	if(m_piWtCntAddS_A)
	{
		LocalFree(m_piWtCntAddS_A);
		m_piWtCntAddS_A=NULL;
	}
	if(m_piWtCntAddS_B)
	{
		LocalFree(m_piWtCntAddS_B);
		m_piWtCntAddS_B=NULL;
	}

    CloseHandle(m_hCompactionComplete);
    CloseHandle(m_hStartCompacting);
    CloseHandle(m_hCompactorThread);
    return TRUE;
}


/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Function:       Compactor::StartCompactor()

Description:    Starts the compactor using the specified thread priority.  The
                compactor thread then attempts to find the the specified number 
                of free sectors.  If bCritical == TRUE, this function doesn't
                return until the compaction stage completes.

Returns:        Boolean indicating success.
------------------------------------------------------------------------------*/
BOOL Compactor::StartCompactor(DWORD dwPriority, DWORD dwFreeSectorsNeeded, BOOL bCritical)
{

    //----- 1. Are there enough DIRTY sectors on the media to fulfill this request? -----
    if(dwFreeSectorsNeeded > m_pSectorMgr->GetNumDirtySectors())
    {
        if(m_pSectorMgr->GetNumDirtySectors() == 0)
        {
            ReportError((TEXT("FLASHDRV.DLL:Compactor::StartCompactor() - There aren't any DIRTY sectors left; the compactor can't be started\r\n")));
            goto START_ERROR;
        }else{
            // Set this compaction operation to retrieve the rest of the DIRTY sectors...
            dwFreeSectorsNeeded = m_pSectorMgr->GetNumDirtySectors();
        }
    }

    //----- 2. Set the number of DIRTY sectors to recycle into FREE sectors -----
    m_dwFreeSectorsNeeded = dwFreeSectorsNeeded;

    m_bCritical = bCritical;

    //----- 3. If this is a critical compaction, make sure the "compaction completed" wait event is reset -----
    if(bCritical == TRUE)
    {
        if(!ResetEvent(m_hCompactionComplete))
        {
            ReportError((TEXT("FLASHDRV.DLL:Compactor::StartCompactor() - Unable to reset the critical Compaction Complete wait event!!!\r\n")));
            goto START_ERROR;
        }
    }

    //----- 4. Signal the compactor to start retrieving the requested number of sectors -----
    if(!SetEvent(m_hStartCompacting))
    {
        ReportError((TEXT("FLASHDRV.DLL:Compactor::StartCompactor() - Unable to signal compactor thread!!!\r\n")));
        goto START_ERROR;
    }

    //----- 5. Set the thread priority accordingly -----
    if(!CeSetThreadPriority(m_hCompactorThread, dwPriority))
    {
        ReportError((TEXT("FLASHDRV.DLL:Compactor::StartCompactor() - Unable to set compactor's thread priority!!!\r\n")));
        goto START_ERROR;
    }

    //----- 6. SPECIAL CASE: If this is a CRITICAL compaction request, we must wait for the compaction process -----
    //                       to complete before returning (this compaction is then SYNCHRONOUSLY executed for the caller).
    if(bCritical == TRUE)
    {
        LeaveCriticalSection( &g_csFlashDevice);

        if(WaitForSingleObject(m_hCompactionComplete, INFINITE) == WAIT_FAILED)
        {
            ReportError((TEXT("FLASHDRV.DLL:CompactorThread() - Unable to wait on m_hCompactionComplete event!\r\n")));
            EnterCriticalSection( &g_csFlashDevice);
            goto START_ERROR;
        }
        EnterCriticalSection( &g_csFlashDevice);
    }
    return TRUE;


START_ERROR:
    return FALSE;
}



/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Function:       Compactor::GetCompactingBlock()

Description:    Retrieves the ID for the block currently being compacted.

Returns:        Boolean indicating success.
------------------------------------------------------------------------------*/
BOOL Compactor::GetCompactingBlock(PBLOCK_ID pBlockID)
{
    //----- 1. Check the parameters to insure they are legitimate -----
    if(pBlockID == NULL)
    {
        ReportError((TEXT("FLASHDRV.DLL:Compactor::GetCompactingBlockID() - pBlockID == NULL !!!\r\n")));
        return FALSE;
    }

    //----- 2. Return the current block being compacted -----
    *pBlockID = m_compactingBlockID;

    return TRUE;
}



/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Function:       Compactor::SetCompactingBlock()

Description:    Specifies which block to use for the next compaction process.

Returns:        Boolean indicating success.
------------------------------------------------------------------------------*/
BOOL Compactor::SetCompactingBlock(BLOCK_ID blockID)
{
    m_compactingBlockID = blockID;
    return TRUE;
}


/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Function:       CompactorThread()

Description:    Compactor thread that does the actual work necessary for coalescing
                the dirty, mapped, and free sectors on the media.

Returns:        Boolean indicating success.
------------------------------------------------------------------------------*/
DWORD Compactor::CompactorThread()
{
    DWORD           dwNumDirtySectorsFound  = 0;
    SECTOR_ADDR     physicalSectorAddr      = 0;
    DWORD           Result = FALSE;
    DWORD           dwWakeReason;
    DWORD           dwTO=INFINITE;

    for(;;)
    {
        //----- 1. Wait for the signal to start compaction process -----
        dwWakeReason=WaitForSingleObject(m_hStartCompacting, dwTO);
        if(dwWakeReason== WAIT_FAILED)
        {
            DEBUGMSG(ZONE_COMPACTOR, (TEXT("FLASHDRV.DLL:CompactorThread() - Unable to wait on m_hStartCompacting event.  Exiting compactor.\r\n")));
            goto COMPACTOR_EXIT;
        }

        if (m_bEndCompactionThread)
        {
            DEBUGMSG(ZONE_COMPACTOR, (TEXT("FLASHDRV.DLL:CompactorThread() - Exiting compactor.\r\n")));
            Result = TRUE;
            goto COMPACTOR_EXIT;
        }            

        // wake up after writen start compact thread
        if(WAIT_TIMEOUT==dwWakeReason)
        {
            // we are in idle for above 2s, compact some dirty sectors
            if( (m_pFal->GetLastAccessTick()+2000)<=GetTickCount())
            {
                // no dirty
                if(m_pSectorMgr->GetNumDirtySectors() < m_pRegion->dwSectorsPerBlock)
                {
                    dwTO=INFINITE;
                    continue;
                }
                m_dwFreeSectorsNeeded= m_pRegion->dwSectorsPerBlock*2;
                CeSetThreadPriority(m_hCompactorThread, g_dwCompactionPrio256);
                DEBUGMSG(ZONE_COMPACTOR, (TEXT("start compact on idle\r\n")));
            }
            // wait for idle 2S
            else
            {
                dwTO=2000;
                continue;
            }
        }
        else
        {
            dwTO=2000;
        }

        //----- For debugging purposes, print out the statistics for this compaction request
#ifdef DEBUG
        EnterCriticalSection(&g_csFlashDevice);
        DEBUGMSG(ZONE_COMPACTOR, (TEXT("FLASHDRV.DLL:------------ Starting Compaction... ------------\r\n")));
        DEBUGMSG(ZONE_COMPACTOR, (TEXT("              Looking to recycle %d DIRTY sectors into FREE sectors\r\n"), m_dwFreeSectorsNeeded));
        DEBUGMSG(ZONE_COMPACTOR, (TEXT("              >>> Media State Information: >>>\r\n")));
        DEBUGMSG(ZONE_COMPACTOR, (TEXT("                 Free  Sectors = %d\r\n"), m_pSectorMgr->GetNumberOfFreeSectors() )); 
        DEBUGMSG(ZONE_COMPACTOR, (TEXT("                 Dirty Sectors = %d\r\n"), m_pSectorMgr->GetNumDirtySectors() )); 
        DEBUGMSG(ZONE_COMPACTOR, (TEXT("FLASHDRV.DLL:------------------------------------------------\r\n")));
        LeaveCriticalSection(&g_csFlashDevice);
#endif

        for(;;)
        {
            DWORD dwStatus = 0;
            DWORD dwBlkCount = 0;
        
            EnterCriticalSection(&g_csFlashDevice);

            //----- 2. Are we done with this compaction process? Compaction is over if the number of -----
            //         requested free sectors have been reclaimed or there are no more DIRTY sectors
            //         left to recycle.
			if((m_dwFreeSectorsNeeded == 0) || (m_pSectorMgr->GetNumDirtySectors() < m_pRegion->dwSectorsPerBlock) )
            {
                LeaveCriticalSection( &g_csFlashDevice);
                break;
            }

            //----- 3. Compact the next block... -----
            m_compactingBlockID = GetNextCompactionBlock(m_compactingBlockID);

            if ((dwBlkCount >= m_pRegion->dwNumPhysBlocks) || (m_compactingBlockID == INVALID_BLOCK_ID))
            {
                m_compactingBlockID = m_pRegion->dwStartPhysBlock;
                RETAILMSG(1, (TEXT("FLASHDRV.DLL:CompactorThread() - CompactorThread(0x%x) failed; unable to compact!!!\r\n"), m_compactingBlockID));
                LeaveCriticalSection( &g_csFlashDevice);
                break;
            }                

            //----- For debugging purposes, print out the compaction block information... -----
            DEBUGMSG(ZONE_COMPACTOR, (TEXT("FLASHDRV.DLL:CompactorThread() - Compacting BLOCK %d\r\n"), m_compactingBlockID));

            //----- 5. Compact the current block... -----
            if((dwNumDirtySectorsFound = CompactBlock(m_compactingBlockID, USE_SECTOR_MAPPING_INFO)) == BLOCK_COMPACTION_ERROR)
            {
                ReportError((TEXT("FLASHDRV.DLL:CompactorThread() - Compactor::CompactBlock(%d) failed; unable to compact!!!\r\n"), m_compactingBlockID));
                LeaveCriticalSection( &g_csFlashDevice);
                break;
            }

            //----- 7. Update the number of DIRTY sectors recycled into FREE sectors while compacting this block -----
            if(m_dwFreeSectorsNeeded < dwNumDirtySectorsFound)
            {
                m_dwFreeSectorsNeeded = 0;                              // Done compacting...
            }else
            {
                m_dwFreeSectorsNeeded -= dwNumDirtySectorsFound;        // Subtract # of recycled DIRTY sectors and keep going...
            }

            LeaveCriticalSection( &g_csFlashDevice);
        }
    
        //----- For debugging purposes, print out the compaction block information... -----
        DEBUGMSG(ZONE_COMPACTOR, (TEXT("FLASHDRV.DLL   >>> Compaction process complete: %d DIRTY sectors recycled \r\n"), dwNumDirtySectorsFound));     


        //----- 9. Signal that the compaction operation is complete... -----
        if(!SetEvent(m_hCompactionComplete))
        {
            ReportError((TEXT("FLASHDRV.DLL:CompactorThread() - Unable to signal compaction is complete!!!\r\n")));
            goto COMPACTOR_EXIT;
        }       

    }

COMPACTOR_EXIT:
    return Result;
}


/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Function:       Compactor::GetNextCompactionBlock()

Description:    Gets the next FLASH block on the media to compact based upon
                the specified "current" compaction block.

Returns:        ID for the next block to compact.
------------------------------------------------------------------------------*/
BLOCK_ID Compactor::GetNextCompactionBlock(BLOCK_ID blockID)
{
    BLOCK_ID nextBlockID;
    static BOOL bRandomBlock = FALSE;
    
    if (m_bCritical) 
    {
        nextBlockID = GetDirtiestBlock(blockID);
    }    
    else
    {        
        // Idle compaction will alternate between compacting the dirtiest block
        // and compacting a random block to provide even wear leveling
        
        if (bRandomBlock)
        {
            nextBlockID = GetNextRandomBlock(blockID);

            // Don't bother to compact a block that is completely free
            if ((nextBlockID == INVALID_BLOCK_ID) || m_pSectorMgr->IsBlockFree (blockID))
            {
                nextBlockID = GetDirtiestBlock(blockID);
            }
        }
        else
        {
            nextBlockID = GetDirtiestBlock(blockID);
        }

        // Toggle the random block flag
        bRandomBlock = !bRandomBlock;        
    }
    
    return nextBlockID;
}

/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Function:       Compactor::GetDirtiestBlock()

Description:    Gets the next FLASH block on the media to compact based upon
                the specified "current" compaction block.

Returns:        ID for the next block to compact.
------------------------------------------------------------------------------*/
BLOCK_ID Compactor::GetDirtiestBlock(BLOCK_ID blockID)
{
    DWORD dwEndBlock = m_pRegion->dwStartPhysBlock + m_pRegion->dwNumPhysBlocks - 1;
    
    DWORD iBlock = (blockID + 1) <= dwEndBlock ? blockID + 1: m_pRegion->dwStartPhysBlock;
    DWORD dwBestBlock = iBlock;
    DWORD dwMaxDirtySectors = 0;
    
    while (iBlock != blockID)
    {
        DWORD dwDirtyCount = m_pSectorMgr->GetDirtyCount (iBlock);
        
        if (dwDirtyCount > dwMaxDirtySectors)
        {
            dwBestBlock = iBlock;
            dwMaxDirtySectors = dwDirtyCount;
        }

        iBlock++;
        if (iBlock > dwEndBlock)
        {
            iBlock = m_pRegion->dwStartPhysBlock;        
        }
    }

    if (!IsBlockWriteable(dwBestBlock))
    {
        RETAILMSG(1, (TEXT("FLASHDRV.DLL:Compactor::GetNextCompactionBlock() Error: Cannot write to block %x!\r\n"), dwBestBlock));
		m_pFal->DumpInfos();
        ASSERT (0);
        return INVALID_BLOCK_ID;
    }    

    return dwBestBlock;
}




/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Function:       Compactor::GetNextRandomBlock()

Description:    Gets the next FLASH block on the media to compact based upon
                the specified "current" compaction block.

Returns:        ID for the next block to compact.
------------------------------------------------------------------------------*/
BLOCK_ID Compactor::GetNextRandomBlock(BLOCK_ID blockID)
{
    DWORD dwNextBlock = INVALID_BLOCK_ID;
    DWORD dwLoopCount = 0;
        
    do
    {           
        dwNextBlock = m_pRegion->dwStartPhysBlock + (Random() % m_pRegion->dwNumPhysBlocks);

        if (++dwLoopCount > m_pRegion->dwNumPhysBlocks)
        {
            return INVALID_BLOCK_ID;
        }
        
    } while (!IsBlockWriteable(dwNextBlock));
  
    return dwNextBlock;
}

DWORD WINAPI CompactorThread(LPVOID lpParameter)
{
    Compactor* compactor = (Compactor*)lpParameter;
    return compactor->CompactorThread();
}

//<<-- ken
// compact blockA to BlockB, at this time B may have valid bata already
// so if rw B error,  then compact A and B to C0, and C0 must be a free block
// if C0 error, simple discard it, and compact A and B to C1...
// all data to be compact must be read from where is original reside
//
// to set block status bad, fmd will try set, if faild fmd will try erase and set again
// for set while not erase first seems more stable
//
int FMDHOOK_GetLastReadErrCode(SECTOR_ADDR errSectorAddr);

DWORD Compactor::CompactBlock(BLOCK_ID compactionBlockID, SECTOR_ADDR treatAsDirtySectorAddr, BOOL fBAdBlock)
{
    SECTOR_ADDR saBlockStart_A, saBlockStart_B=INVALID_SECTOR_ADDR;
    DWORD dwCompStartOff_A, dwCompStartOff_B, dwCompEndOff_A, dwCompEndOff_B;

    DWORD dwBlockB=INVALID_BLOCK_ID;
    DWORD dwWtBlk;

    SectExInfo sectExInfo;
	BOOL fCached;
	
    SectorMappingInfo sectorMappingInfo;    
    
    SECTOR_ADDR saCur, saNew, saExist;

    DWORD dwDirtySect;
    
    BOOL fCompactB=FALSE;
    BOOL fCompactAOk=FALSE;
    BOOL fCompactBOk=TRUE;
    BOOL fBadBlockA=fBAdBlock;

	DWORD i;

    // Record compaction in CELOG
    CELOG_CompactBlock(m_bCritical, compactionBlockID);

	// the write count add as lest "1"
	for( i=0; i<m_pRegion->dwSectorsPerBlock; i++)
	{
		m_piWtCntAddS_A[i]=1;
		m_piWtCntAddS_B[i]=1;
	}

	dwDirtySect=m_pSectorMgr->GetNumDirtySectors();
	
    saBlockStart_A = m_pSectorMgr->GetStartSectorInBlock (compactionBlockID);
	dwCompStartOff_A=0;
	dwCompEndOff_A=m_pRegion->dwSectorsPerBlock;
    
    DEBUGMSG(ZONE_COMPACTOR, (TEXT("CompactBlock block:0x%x, treatAsDirtySectorAddr:0x%x, all free:0x%x, dirty:0x%x, mapped:0x%x, unusable blocks:0x%x\r\n")\
    , compactionBlockID, treatAsDirtySectorAddr, m_pSectorMgr->GetNumberOfFreeSectors(), m_pSectorMgr->GetNumDirtySectors(), m_pMap->GetMappedNumb(), m_pSectorMgr->GetNumUnusableBlocks()));
    m_pSectorMgr->UnmarkSectorsAsFree(saBlockStart_A, m_pRegion->dwSectorsPerBlock);

    do
    {
        if(FALSE==fCompactAOk)
        {
	        for( i=dwCompStartOff_A; i<dwCompEndOff_A; i++)
	        {
				saCur=saBlockStart_A+i;

				BOOL fRdErr=FALSE;
	            //----- 3. Read the sector mapping information for the current physical sector -----
	            if(!FMD.pReadSector(saCur, m_pSectorData, (PSectorInfo)&sectorMappingInfo, 1))
	            {
	                // unmark all dirty sectors
	                if(FMDAK_READ_RET_ERR_BLOCK_BAD==FMDHOOK_GetLastReadErrCode(saCur))
	                {
	                    fBadBlockA=TRUE;
	                }

	                SECTOR_ADDR sectAddr=m_pMap->GetLogicalSectAddr(saCur);
					// this physical sector is not mapping so dirty or free sector now trade as dirty,
					// for this will be mark as free or unusable anyway
	                if(UNMAPPED_LOGICAL_SECTOR==sectAddr)
	                {
						RETAILMSG(1, (TEXT("!!!unmapped physical sector:0x%x read error when compact!!!\r\n"), saCur));
	                    continue;
	                }
					// mapped physical sector must be a valid data sector
	                else
	                {
	                    DEBUGMSG(ZONE_COMPACTOR, (TEXT("compact read error sector logical:0x%x physical:0x%x \r\n"), sectAddr, saCur));
	                    memset(&sectorMappingInfo, 0xff, sizeof(sectorMappingInfo));
	                    sectorMappingInfo.logicalSectorAddr=sectAddr;
	                    sectorMappingInfo.fDataStatus &= (~SECTOR_WRITE_COMPLETED);
	                    if(!m_pFal->GetWtCnt( sectAddr, &(sectorMappingInfo.exInfo), &fCached))
	                    {
							sectorMappingInfo.exInfo.iWtCnt=m_pFal->GetMaxWtCnt();
	                    }
						// this is a deleted sector, tread as dirty
						else if(0==sectorMappingInfo.exInfo.del)
						{
							DEBUGMSG(ZONE_COMPACTOR, (TEXT("compact read error sector logical:0x%x physical:0x%x had been deleted!\r\n"), sectAddr, saCur));
							continue;
						}

                        sectorMappingInfo.exInfo.rdErr=0;   // a read error sector!
	                }

					fRdErr=TRUE;
	            }


	            // all sector after this must free also
	            if(IsSectorFree(sectorMappingInfo))
	            {
                    DEBUGMSG(ZONE_COMPACTOR, (TEXT("!!! compact find free sector at:0x%x !!!\r\n"), saCur));
					continue;
	            }
	            
	            //----- 4. DIRTY sector?  Unmark this sector as DIRTY (using the Sector Manager) because -----
	            //         we are going to recycle it into a FREE sector through an erase operation.
	            if( IsSectorDirty(sectorMappingInfo)
					|| ((treatAsDirtySectorAddr != USE_SECTOR_MAPPING_INFO) && (treatAsDirtySectorAddr == saCur))
					|| (!m_pFal->GetWtCnt(sectorMappingInfo.logicalSectorAddr, &sectExInfo, &fCached) )	// can't get write count trade as dirty
					|| (0==sectExInfo.del)										// deleted sector
					|| (sectExInfo.iWtCnt>=(sectorMappingInfo.exInfo.iWtCnt+m_piWtCntAddS_A[i])) )	// dirty write count
	            {
                    DEBUGMSG( ZONE_COMPACTOR, (TEXT("compact find a %s flag sector:0x%x\r\n"), (0==sectExInfo.del)?TEXT("deleted"):TEXT("dirty"), saCur));
					// read error sector trade as not dirty
					if(!fRdErr)
					{
		                continue;
					}
	            }

	            //
	            // not free not dirty so valid data
	            //
	            if(!m_pMap->IsValidLogicalSector(sectorMappingInfo.logicalSectorAddr))
	            {
	                ReportError((TEXT("FLASHDRV.DLL     >>> Logical sector(=0x%08x) of physical sector(=0x%08x) is incorrect.\r\n"), sectorMappingInfo.logicalSectorAddr, saCur));
	                goto COMPACTION_ERROR;
	            }

	            //----- 7. Get a free physical sector to store the data into -----
	            if(!m_pSectorMgr->GetNextFreeSector(&saNew, TRUE))
	            {
	                ReportError((TEXT("FLASHDRV.DLL:Compactor::CompactBlock() - Unable to get next free physical sector address for writing!  The media is FULL\r\n")));
	                goto COMPACTION_ERROR;
	            }

	            // init sectorB info
	            if(INVALID_BLOCK_ID==dwBlockB)
	            {
					dwBlockB=m_pSectorMgr->GetBlockFromSector(saNew);
					saBlockStart_B=m_pSectorMgr->GetStartSectorInBlock(dwBlockB);
					dwCompStartOff_B=0;
	                dwCompEndOff_B=saNew-saBlockStart_B;
	            }

	            //----- 8. Start the write operation 
	            dwWtBlk=m_pSectorMgr->GetBlockFromSector(saNew);
	            sectorMappingInfo.exInfo.iWtCnt+=m_piWtCntAddS_A[i];
	            if(!FMD.pWriteSector(saNew, m_pSectorData, (PSectorInfo)&sectorMappingInfo, 1))
	            {
	                // write faild in block B, compact B later
	                if(dwBlockB==dwWtBlk)
	                {
	                    m_pSectorMgr->UnmarkSectorsAsFree(saBlockStart_B, m_pRegion->dwSectorsPerBlock);
                        RETAILMSG( 1, (TEXT("compact block A write faild in BlockB:0x%x !!!\r\n"), dwBlockB));
	                    fCompactB=TRUE;
						fCompactBOk=FALSE;
	                }
	                else
	                {
                        RETAILMSG( 1, (TEXT("compact block A write faild in BlockC:0x%x !!!\r\n"), dwWtBlk));
	                    m_pSectorMgr->UnmarkSectorsAsFree(m_pSectorMgr->GetStartSectorInBlock(dwWtBlk), m_pRegion->dwSectorsPerBlock);
	                    // no dirty to do    
	                    // EraseBlock(dwWtBlk);
	                    FMD.pSetBlockStatus(dwWtBlk, BLOCK_STATUS_BAD);
	                    m_pSectorMgr->MarkBlockUnusable(dwWtBlk);
	                }

                    RETAILMSG( 1, (TEXT("restart compact A!!!\r\n")));
	                break;
	            }
	            else
	            {
	                if( (m_pSectorMgr->GetStartSectorInBlock(dwWtBlk)+m_pRegion->dwSectorsPerBlock-1)==saNew)
	                {
	                // last sector of block, update infors
	                    dwCompStartOff_A=i+1;
	                }


	                //----- 10. Perform the LOGICAL -> PHYSICAL mapping -----
	                if(!m_pMap->MapLogicalSector(sectorMappingInfo.logicalSectorAddr, saNew, &saExist))
	                {
	                    goto COMPACTION_ERROR;
	                }
                    DEBUGMSG(ZONE_COMPACTOR,(TEXT("compact block A logical:0x%x, physical:0x%x, exist:0x%x, Write count:0x%x\r\n"), sectorMappingInfo.logicalSectorAddr, saNew, saExist, sectorMappingInfo.exInfo.iWtCnt));

	                m_piWtCntAddS_A[i]++;	// write count will be add for this compact write!

					m_pFal->ChckChgMaxCnt(sectorMappingInfo.exInfo.iWtCnt);
					m_pFal->CacheWtCnt(sectorMappingInfo.logicalSectorAddr, &sectorMappingInfo.exInfo);
					
					RETAILMSG(((saExist!=saCur) && (1==m_piWtCntAddS_A[i])),(TEXT("!!! compact block A logical:0x%x to physical:0x%x, Write count:0x%x, existphysical:0x%x != orgphysical:0x%x\r\n"), sectorMappingInfo.logicalSectorAddr, saNew, sectorMappingInfo.exInfo.iWtCnt, saExist, saCur));
					ASSERT(!((saExist!=saCur) && (1==m_piWtCntAddS_A[i])));
	            }

	        }   
			
			if( i==dwCompEndOff_A )
			{
				fCompactAOk=TRUE;
			}
        }

		//
		// now compact block B
        if((TRUE==fCompactAOk) && fCompactB)
        {
			for( i=dwCompStartOff_B; i<dwCompEndOff_B; i++)
            {
				saCur=saBlockStart_B+i;
				
				BOOL fRdErr=FALSE;
                //----- 3. Read the sector mapping information for the current physical sector -----
                if(!FMD.pReadSector(saCur, m_pSectorData, (PSectorInfo)&sectorMappingInfo, 1))
				{
	                // unmark all dirty sectors
	                SECTOR_ADDR sectAddr=m_pMap->GetLogicalSectAddr(saCur);
					// this physical sector is not mapping so dirty or free sector now trade as dirty,
					// for this will be mark as free or unusable anyway
	                if(UNMAPPED_LOGICAL_SECTOR==sectAddr)
	                {
						RETAILMSG(1, (TEXT("!!!unmapped physical sector:0x%x read error when compact!!!\r\n"), saCur));
	                    continue;
	                }
					// mapped physical sector must be a valid data sector
	                else
	                {
	                    DEBUGMSG(ZONE_COMPACTOR, (TEXT("compact read error sector logical:0x%x physical:0x%x \r\n"), sectAddr, saCur));
	                    memset(&sectorMappingInfo, 0xff, sizeof(sectorMappingInfo));
	                    sectorMappingInfo.logicalSectorAddr=sectAddr;
	                    sectorMappingInfo.fDataStatus &= (~SECTOR_WRITE_COMPLETED);
	                    if(!m_pFal->GetWtCnt( sectAddr, &(sectorMappingInfo.exInfo), &fCached))
	                    {
							sectorMappingInfo.exInfo.iWtCnt=m_pFal->GetMaxWtCnt();
	                    }
						// this is a deleted sector, tread as dirty
						else if(0==sectorMappingInfo.exInfo.del)
						{
							DEBUGMSG(ZONE_COMPACTOR, (TEXT("compact read error sector logical:0x%x physical:0x%x had been deleted!\r\n"), sectAddr, saCur));
							continue;
						}

                        sectorMappingInfo.exInfo.rdErr=0;   // a read error sector!
	                }

					fRdErr=TRUE;
	            }                

                // all sector after this must free also
                if(IsSectorFree(sectorMappingInfo))
                {
                    DEBUGMSG(ZONE_COMPACTOR, (TEXT("compact find free sector at:0x%x\r\n"), saCur));
					continue;
                }

                //----- 4. DIRTY sector?  Unmark this sector as DIRTY (using the Sector Manager) because -----
                //         we are going to recycle it into a FREE sector through an erase operation.
	            if( IsSectorDirty(sectorMappingInfo)
					|| ((treatAsDirtySectorAddr != USE_SECTOR_MAPPING_INFO) && (treatAsDirtySectorAddr == saCur))
					|| (!m_pFal->GetWtCnt(sectorMappingInfo.logicalSectorAddr, &sectExInfo, &fCached) )	// can't get write count trade as dirty
					|| (0==sectExInfo.del)												// deleted sector
					|| (sectExInfo.iWtCnt>=(sectorMappingInfo.exInfo.iWtCnt+m_piWtCntAddS_B[i])) )	// dirty write count
                {
                    DEBUGMSG( ZONE_COMPACTOR, (TEXT("compact find a %s flag sector:0x%x\r\n"), (0==sectExInfo.del)?TEXT("deleted"):TEXT("dirty"), saCur));
					// read error sector trade as not dirty
					if(!fRdErr)
					{
		                continue;
					}
                }

                //
                // not free not dirty so valid data
                //
                if(!m_pMap->IsValidLogicalSector(sectorMappingInfo.logicalSectorAddr))
                {
                    ReportError((TEXT("FLASHDRV.DLL     >>> Logical sector(=0x%08x) of physical sector(=0x%08x) is incorrect.\r\n"), sectorMappingInfo.logicalSectorAddr, saCur));
                    goto COMPACTION_ERROR;
                }

                //----- 7. Get a free physical sector to store the data into -----
                if(!m_pSectorMgr->GetNextFreeSector(&saNew, TRUE))
                {
                    ReportError((TEXT("FLASHDRV.DLL:Compactor::CompactBlock() - Unable to get next free physical sector address for writing!  The media is FULL\r\n")));
                    goto COMPACTION_ERROR;
                }

                //----- 8. Start the write operation 
                dwWtBlk=m_pSectorMgr->GetBlockFromSector(saNew);
                sectorMappingInfo.exInfo.iWtCnt+=m_piWtCntAddS_B[i];
                if(!FMD.pWriteSector(saNew, m_pSectorData, (PSectorInfo)&sectorMappingInfo, 1))
                {
                    RETAILMSG( 1, (TEXT("compact block B write faild in BlockC:0x%x !!!\r\n"), dwWtBlk));
                    m_pSectorMgr->UnmarkSectorsAsFree(m_pSectorMgr->GetStartSectorInBlock(dwWtBlk), m_pRegion->dwSectorsPerBlock);
                    // no dirty to do    
					// fmd will set status first, or set erase set when first faild
                    //EraseBlock(dwWtBlk);
                    if(!FMD.pSetBlockStatus(dwWtBlk, BLOCK_STATUS_BAD))
                    {
                        RETAILMSG( 1, (TEXT("BlockC:0x%x set bad error!!!\r\n"), dwWtBlk));
                    }
                    m_pSectorMgr->MarkBlockUnusable(dwWtBlk);
                    fCompactAOk=FALSE;
                    fCompactBOk=FALSE;

                    RETAILMSG( 1, (TEXT("restart compact A!!!\r\n")));
                    break;
                }
                else
                {
                    if( (m_pSectorMgr->GetStartSectorInBlock(dwWtBlk)+m_pRegion->dwSectorsPerBlock-1)==saNew)
                    {
                    // last sector of block, update infors
						dwCompStartOff_B=i+1;
                    }

                    //----- 10. Perform the LOGICAL -> PHYSICAL mapping -----
                    if(!m_pMap->MapLogicalSector(sectorMappingInfo.logicalSectorAddr, saNew, &saExist))
                    {
                        goto COMPACTION_ERROR;

                    }
                    DEBUGMSG(ZONE_COMPACTOR,(TEXT("compact block B logical:0x%x, physical:0x%x, exist:0x%x, Write count:0x%x\r\n"), sectorMappingInfo.logicalSectorAddr, saNew, saExist, sectorMappingInfo.exInfo.iWtCnt));

					m_pFal->ChckChgMaxCnt(sectorMappingInfo.exInfo.iWtCnt);
	                m_piWtCntAddS_B[i]++;	// write count will be add for this compact faild while write count maybe updated this try!

					m_pFal->CacheWtCnt(sectorMappingInfo.logicalSectorAddr, &sectorMappingInfo.exInfo);
					
					RETAILMSG(((saExist!=saCur) && (1==m_piWtCntAddS_B[i])),(TEXT("!!! compact block B logical:0x%x to physical:0x%x, Write count:0x%x, existphysical:0x%x != orgphysical:0x%x\r\n"), sectorMappingInfo.logicalSectorAddr, saNew, sectorMappingInfo.exInfo.iWtCnt, saExist, saCur));
					ASSERT(!((saExist!=saCur) && (1==m_piWtCntAddS_B[i])));
                }

            }
			if(i==dwCompEndOff_B)
			{
				fCompactBOk=TRUE;
			}
        }

    }while((!fCompactAOk) || (!fCompactBOk));

    //----- 13. Compaction on this block is complete.  Erase the block and recycle the DIRTY sectors -----
    //          into FREE sectors.
    m_pSectorMgr->UnmarkSectorsAsDirty(m_pSectorMgr->GetStartSectorInBlock(compactionBlockID), m_pSectorMgr->GetDirtyCount(compactionBlockID));
    if(fBadBlockA)
    {
        RETAILMSG( 1, (TEXT("BlockA:0x%x bad!!!\r\n"), compactionBlockID));
		// fmd will set status first, or set erase set when first faild
		//EraseBlock(compactionBlockID);
        if(!FMD.pSetBlockStatus(compactionBlockID, BLOCK_STATUS_BAD))
        {
            RETAILMSG( 1, (TEXT("BlockA:0x%x set bad error!!!\r\n"), compactionBlockID));
        }
        m_pSectorMgr->MarkBlockUnusable(compactionBlockID);
    }
    else
    {
        if(FMD.pEraseBlock(compactionBlockID))
        {
            if(!m_pSectorMgr->AddSectorsToList (SectorMgr::Free, m_pSectorMgr->GetStartSectorInBlock(compactionBlockID), m_pRegion->dwSectorsPerBlock))
            {
                ReportError((TEXT("FLASHDRV.DLL:Compactor::CompactBlock() - Unable to mark physical sectors 0x%08x - 0x%08x as free...\r\n"), 
                                   m_pSectorMgr->GetStartSectorInBlock(compactionBlockID), (m_pSectorMgr->GetStartSectorInBlock(compactionBlockID) + m_pRegion->dwSectorsPerBlock) ));
                goto COMPACTION_ERROR;
            }
        }
        else
        {
            RETAILMSG( 1, (TEXT("BlockA:0x%x erase error!!!\r\n"), compactionBlockID));
            if(!FMD.pSetBlockStatus(compactionBlockID, BLOCK_STATUS_BAD))
            {
                RETAILMSG( 1, (TEXT("BlockA:0x%x set bad error!!!\r\n"), compactionBlockID));
            }
            m_pSectorMgr->MarkBlockUnusable(compactionBlockID);
        }
    }
    
    if(fCompactB)
    {
        RETAILMSG( 1, (TEXT("BlockB:0x%x bad!!!\r\n"), dwBlockB));
        m_pSectorMgr->UnmarkSectorsAsDirty(m_pSectorMgr->GetStartSectorInBlock(dwBlockB), m_pSectorMgr->GetDirtyCount(dwBlockB));
     //erase B
	 // fmd will set status first, or set erase set when first faild
//        EraseBlock(dwBlockB);
        if(!FMD.pSetBlockStatus(dwBlockB, BLOCK_STATUS_BAD))
        {
            RETAILMSG( 1, (TEXT("BlockB:0x%x set bad error!!!\r\n"), dwBlockB));
        }
        m_pSectorMgr->MarkBlockUnusable(dwBlockB);
    }

	DEBUGMSG(ZONE_COMPACTOR, (TEXT("CompactBlock block:0x%x, find :0x%x dirty, all free:0x%x, dirty:0x%x, mapped:0x%x, unusable blocks:0x%x\r\n")\
		, compactionBlockID, dwDirtySect-m_pSectorMgr->GetNumDirtySectors(), m_pSectorMgr->GetNumberOfFreeSectors(), m_pSectorMgr->GetNumDirtySectors(), m_pMap->GetMappedNumb(), m_pSectorMgr->GetNumUnusableBlocks()));

    return dwDirtySect-m_pSectorMgr->GetNumDirtySectors();

COMPACTION_ERROR:
    return BLOCK_COMPACTION_ERROR;
}


